'''
Created on 06/10/2013

@author: daniel
'''
import unittest
import colecciones.listaEnlazada


class HashTableAbierto():
    '''
    Clase abstracta que encapsula las condiciones de una hashtable, 
    deja la implementacion de la estrategia de dispersion para la subclase
    '''
    def __init__(self,n):
        self.__tabl__=[None]*n
        
    def noCollitions(self,i):
        '''
        devuelve True si el indice esta vacio;
        False si hay una colision (o sea contiene una ListaEnlazada);
        o el valor del elemento si contiene uno solo
        '''
        if(self.__tabl__[i]==None):return True
        if(isinstance(self.__tabl__[i],colecciones.listaEnlazada.ListaEnlazada)):return False
        return self.__tabl__[i]
    
    def newCollition(self,i,elem):
        '''
        en la posicion i ocupada por un elemento, lo reemplaza con una listaEnlazada
        y agrega el nuevo elemento
        '''
        aux=self.__tabl__[i]
        self.__tabl__[i]=colecciones.listaEnlazada.ListaEnlazada()
        self.__tabl__[i].agregar(aux,0)
        self.__tabl__[i].agregar(elem,1)

    def hashAdd(self,elem):
        raise NotImplementedError(u"el metodo hashAdd no esta implementado en HashTableAbierto")

    def hashChk(self,elem):
        raise NotImplementedError(u"el metodo hashChk no esta implementado en HashTableAbierto")
    
    def __collisAt__(self,i,elem):
        '''
        metodo para colisionar elementos a la dispersion que va a ser usado por el 
        metodo concreto hashAdd
        '''
        st=self.noCollitions(i)
        if(st==True):self.__tabl__[i]=elem
        elif(st==False):self.__tabl__[i].agregar(elem,self.__tabl__[i].getTamanio())
        else:self.newCollition(i, elem)

    def __setAt__(self,i,elem):
        '''
        metodo para ubicar forzadamente elementos a la dispersion que va a ser usado por el 
        metodo concreto hashAdd
        '''
        self.__tabl__[i]=elem
        
    def __getAt__(self,i):
        '''
        metodo para devolver elementos de la dispersion que va a ser usado por el 
        metodo concreto hashChk
        '''
        return self.__tabl__[i]
    
class Test(unittest.TestCase):

    def testInit(self):
        self.assertIsInstance(HashTableAbierto(5), HashTableAbierto, "Verifica la correcta creacion")

    def testNoCollitions(self):
        obj=HashTableAbierto(5)
        obj.__tabl__[2]=3
        obj.__tabl__[3]=colecciones.listaEnlazada.ListaEnlazada()
        self.assertEquals(obj.noCollitions(2), 3, "Verifica devolucion correcta")
        self.assertFalse(obj.noCollitions(3), "Verifica que si hay colicion")
        self.assertTrue(obj.noCollitions(0), "Verifica que no hay colicion")
        
    def testNewColition(self):
        obj=HashTableAbierto(5)
        obj.__tabl__[2]=3
        obj.__tabl__[3]=colecciones.listaEnlazada.ListaEnlazada()
        self.assertEquals(obj.noCollitions(2), 3, "Verifica devolucion correcta")
        self.assertFalse(obj.noCollitions(3), "Verifica que si hay colicion")
        obj.newCollition(2,4)
        self.assertFalse(obj.noCollitions(2), "Verifica que si hay colicion")
#         print obj.__tabl__[2]
        self.assertTrue(obj.noCollitions(0), "Verifica que no hay colicion")
        
    def testUpAt(self):
        obj=HashTableAbierto(5)
        obj.__tabl__[2]=3
        obj.__tabl__[3]=colecciones.listaEnlazada.ListaEnlazada()
        self.assertEquals(obj.noCollitions(2),3, "Verifica devolucion correcta")
        obj.__collisAt__(2,5)
        self.assertEquals(obj.__tabl__[2].elemento(0),3, "Verifica devolucion correcta")
        self.assertEquals(obj.__tabl__[2].elemento(1),5, "Verifica devolucion correcta")
        self.assertTrue(obj.noCollitions(0), "Verifica que no hay colicion")
        obj.__collisAt__(0,"a")
        self.assertEquals(obj.noCollitions(0),"a", "Verifica devolucion correcta")
        obj.__collisAt__(3,"b")
        self.assertFalse(obj.noCollitions(3), "Verifica que si hay colicion")
        self.assertEquals(obj.__tabl__[3].elemento(0),"b", "Verifica devolucion correcta")

    def testSetAt(self):
        obj=HashTableAbierto(5)
        obj.__tabl__[2]=3
        obj.__tabl__[3]=colecciones.listaEnlazada.ListaEnlazada()
        self.assertEquals(obj.noCollitions(2),3, "Verifica devolucion correcta")
        obj.__setAt__(2,5)
        self.assertEquals(obj.__tabl__[2],5, "Verifica devolucion correcta")
        self.assertTrue(obj.noCollitions(0), "Verifica que no hay colicion")
        obj.__setAt__(0,"a")
        self.assertEquals(obj.noCollitions(0),"a", "Verifica devolucion correcta")
        obj.__setAt__(3,"b")
        self.assertEquals(obj.__tabl__[3],"b", "Verifica devolucion correcta")

        
if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()